package top.milkbox.core;

import java.io.*;

/**
 * 有关文件的操作工具
 * 文件编码的操作
 * 文件类型的操作
 * <p>
 * 创建时间 2023年2月10日
 * 最后修改 2023年2月10日
 *
 * @author milkbox
 */
public class UtilsFile {

    /*

        // 字符编码转换举例如下
        public static void main(String[] args) {

            String readPath = "E:/my_temp/jilu.txt";
            String writePath = "E:/my_temp/jilu_other.txt";
            String readCharset = "GBK";
            String writeCharset = "UTF-8";

            try {
                System.out.println("当前文件可能的编码：" + getFileCharsetName(readPath));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            transform(readPath, writePath, readCharset, writeCharset, false);

        }
     */


    /**
     * 编码转换
     *
     * @param readPath     源文件路径
     * @param writePath    转换后文件路径
     * @param readCharset  源文件编码类型
     * @param writeCharset 要转换为什么类型编码
     * @param isPreview    是否在转换过程中预览
     * @author guo
     */
    public static void transform(
            String readPath, String writePath,
            String readCharset, String writeCharset,
            boolean isPreview
    ) throws Exception {
        File readFile = new File(readPath);
        File writeFile = new File(writePath);

        FileInputStream fileInputStream = null;
        InputStreamReader inputStreamReader = null;

        FileOutputStream fileOutputStream = null;
        OutputStreamWriter outputStreamWriter = null;

        try {
            // 创建字节输入流
            fileInputStream = new FileInputStream(readFile);
            // 创建转换流，将字节输入流转换为字符输入流，并指定读取使用的字符集
            inputStreamReader = new InputStreamReader(fileInputStream, readCharset);

            // 创建字节输出流
            fileOutputStream = new FileOutputStream(writeFile);
            // 创建转换流，将字节输出流转换为字符输出流，并指定写入使用的字符集
            outputStreamWriter = new OutputStreamWriter(fileOutputStream, writeCharset);

            char[] chars = new char[8];
            int len;
            while ((len = inputStreamReader.read(chars)) != -1) {
                // 预览
                if (isPreview) {
                    String value = String.valueOf(chars, 0, len);
                    System.out.print(value);
                }

                // 写入
                outputStreamWriter.write(chars, 0, len);
            }
        } catch (Exception e) {
            throw new Exception("转码出现异常，请检查文件的路径以及编码");
        } finally {
            // 关闭输入流
            if (inputStreamReader != null) {
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            // 关闭输出流
            if (outputStreamWriter != null) {
                try {
                    outputStreamWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 判断文本文件编码的重载
     *
     * @param filePath 文件的全路径
     * @return 返回编码类型字符串
     * @throws Exception 文件未找到获取所选的不是文件
     * @author 改编自互联网
     */
    public static String getFileCharsetName(String filePath) throws Exception {
        return getFileCharsetName(new File(filePath));
    }

    /**
     * 判断文本文件编码的重载
     *
     * @param file File类
     * @return 返回编码类型字符串
     * @throws Exception 文件未找到获取所选的不是文件
     * @author 改编自互联网
     */
    public static String getFileCharsetName(File file) throws Exception {
        if (!file.isFile()) {
            throw new Exception("所选的不是文件");
        }
        return getFileCharsetName(new FileInputStream(file));
    }

    /**
     * 判断文本文件的字符编码，注意：并非100%准确
     *
     * @param fileInputStream 文本文件字节输入流
     * @return 返回编码类型字符串
     * @author 改编自互联网
     */
    public static String getFileCharsetName(FileInputStream fileInputStream) {
        BufferedInputStream bis = null;
        String charset = "GBK";
        byte[] first3Bytes = new byte[3];
        try {
            boolean checked = false;
            bis = new BufferedInputStream(fileInputStream);
            bis.mark(0); // 读者注： bis.mark(0);修改为 bis.mark(100);我用过这段代码，需要修改上面标出的地方。
            int read = bis.read(first3Bytes, 0, 3);
            if (read == -1) {
                bis.close();
                return charset; // 文件编码为 ANSI
            } else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
                charset = "UTF-16LE"; // 文件编码为 Unicode
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
                charset = "UTF-16BE"; // 文件编码为 Unicode big endian
                checked = true;
            } else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB
                    && first3Bytes[2] == (byte) 0xBF) {
                charset = "UTF-8"; // 文件编码为 UTF-8
                checked = true;
            }
            bis.reset();
            if (!checked) {
                while ((read = bis.read()) != -1) {
                    if (read >= 0xF0) {
                        break;
                    }
                    if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的，也算是GBK
                    {
                        break;
                    }
                    if (0xC0 <= read && read <= 0xDF) {
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
                        {
                            // (0x80 - 0xBF),也可能在GB编码内
                            continue;
                        } else {
                            break;
                        }
                    } else if (0xE0 <= read && read <= 0xEF) { // 也有可能出错，但是几率较小
                        read = bis.read();
                        if (0x80 <= read && read <= 0xBF) {
                            read = bis.read();
                            if (0x80 <= read && read <= 0xBF) {
                                charset = "UTF-8";
                                break;
                            } else {
                                break;
                            }
                        } else {
                            break;
                        }
                    }
                }
            }
            bis.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return charset;
    }

    /**
     * 获取文件扩展名
     * 例如：
     * 列子.2022.txt 论语.2022.09.24.txt 这些都是“.txt”，所以level设置为1
     * java.8.11.tar.gz mysql.5.7.tar.gz 这种的都是“.tar.gz”，所以level设置为2
     *
     * @param fileName 文件名，全名
     * @param level    要获取的扩展名的个数
     * @return 返回扩展名（带点），失败则返回null
     */
    public static String getFileExtension(String fileName, Integer level) {
        String[] strList = fileName.split("\\.");
        if (strList.length <= 1) {
            System.out.println("文件名没有扩展名");
            return null;
        } else if (level <= 0 || level > strList.length) {
            System.out.println("level设置不正确");
            return null;
        }
        StringBuilder extension = new StringBuilder();
        while (level >= 1) {
            extension.append(".");
            extension.append(strList[strList.length - level]);
            level--;
        }
        return extension.toString();
    }

    /**
     * 获取文件扩展名
     *
     * @param fileName 文件全名
     * @return 返回扩展名（带点），失败返回null
     */
    public static String getFileExtension(String fileName) {
        return getFileExtension(fileName, 1);
    }

    /**
     * 获取.之前的名字，例如：file.txt -> file
     *
     * @param fileName 文件名
     * @return 返回文件名
     */
    public static String getFileNameBeforeDot(String fileName) {
        return fileName.split("\\.")[0];
    }

    public static String getFileNameBeforeDot(File file) {
        return getFileNameBeforeDot(file.getName());
    }

    public static String appendFileNameBeforeDot(String fileName, String otherString) {
        return getFileNameBeforeDot(fileName) + otherString + getFileExtension(fileName);
    }

    /**
     * 文件移动，将oldPath移动到newPath中，如果文件名不一样则会重命名
     * 目标目录必须存在，目标目录下不能有重名文件
     *
     * @param oldPath 原来的文件磁盘路径，例如：C://file/file/text.txt
     * @param newPath 新的文件磁盘路径，例如：C://box/box/list.txt
     * @return 成功true
     */
    public static Boolean removeFile(String oldPath, String newPath) {
        return new File(oldPath).renameTo(new File(newPath));
    }

}
